<?php
// $Id: IbRoles.php 75 2008-08-27 09:56:03Z hugo.wetterberg $

define('IB_ROLES_STRING', 1);
define('IB_ROLES_TEXT', 2);
define('IB_ROLES_NUMBER', 3);
define('IB_ROLES_OBJECT', 4);

define('IB_ROLES_ERR_COLUMN_EXISTS',-1001);
define('IB_ROLES_ERR_COLUMN_DOESNT_EXIST',-1002);
define('IB_ROLES_ERR_UNKNOWN_COLUMN_TYPE',-1003);
define('IB_ROLES_ERR_INVALID_COLUMN_NAME',-1004);
define('IB_ROLES_ERR_DATABASE_ERROR',-1005);
define('IB_ROLES_ERR_TYPE_MISMATCH',-1006);
define('IB_ROLES_ERR_NO_SUCH_USER',-1007);

class IbRoles {
  private static $configuration;
  
  /**
   * Loads the ib_roles configuration if it is not loaded
   *
   * @return void
   * @author Hugo Wetterberg
   **/
  public static function load_configuration() {
    if (!isset(IbRoles::$configuration)) {
      $conf = variable_get('ib_roles_configuration', false);
      if($conf) {
        IbRoles::$configuration = unserialize($conf);
      }
      else {
        IbRoles::$configuration = (object)array(
          'columns'=>array(),
          'groups'=>array(),
        );
      }
    }
  }
  
  /**
   * Saves the ib_roles configuration
   *
   * @return void
   * @author Hugo Wetterberg
   **/
  private static function save_configuration() {
    variable_set('ib_roles_configuration', serialize(IbRoles::$configuration));
  }
  
  /**
   * Checks if a column exists
   *
   * @return bool True if the column exists, False if it does not
   * @author Hugo Wetterberg
   **/
  public static function column_exists($name) {
    return isset(IbRoles::$configuration->columns[$name]);
  }
  
  /**
   * Helper function to get sql types for IB_ROLES_ type values
   *
   * @param int $type One of the IB_ROLES_ type constants
   *
   * @return void
   * @author Hugo Wetterberg
   **/
  private static function type_string($type) {
    switch ($type) {
      case IB_ROLES_STRING:
        return 'varchar(255)';
      case IB_ROLES_TEXT:
        return 'text';
      case IB_ROLES_OBJECT:
        return 'longtext';
      case IB_ROLES_NUMBER:
        return 'float';
    }
  }
  
  /**
   * Adds a information column
   * 
   * @param string $name The name of the column
   * @param string $label The label to use for the column
   * @param int $type One of the IB_ROLES_ type constants
   * 
   * @return void
   * @author Hugo Wetterberg
   **/
  public static function add_column($name, $label, $type=IB_ROLES_STRING) {
    if (IbRoles::column_exists($name)) {
      throw new Exception(t('The column you tried to add already exists'), IB_ROLES_ERR_COLUMN_EXISTS);
    }
    
    //Validate the column name
    //This together with prefixing column_ for the actual column names should
    //protect us against any sql injection or name collision with reserved words
    if(!preg_match('/^[a-z_]+$/', $name)) {
      throw new Exception(t('The column name may only contain letters (a-z) and underscores'), IB_ROLES_ERR_INVALID_COLUMN_NAME);
    }
    
    //Get the sql type for the type value
    $type_string = IbRoles::type_string($type);
    if (!isset($type_string)) {
      throw new Exception(t('The column type "@type" is unknown', array('@type'=>$type)), IB_ROLES_ERR_UNKNOWN_COLUMN_TYPE);
    }
    
    //Alter our data table
    $result = db_query("ALTER TABLE {ib_roles_data} ADD COLUMN column_{$name} {$type_string} NULL");
    if (!$result) {
      throw new Exception(t('Could not add the column "@name"', array('@name'=>$name)), IB_ROLES_ERR_DATABASE_ERROR);
    }
    
    //Update and save configuration
    IbRoles::$configuration->columns[$name] = array(
      'name' => $name,
      'label' => $label,
      'type' => $type,
      'groups' => array(),
      'edit_groups' => array(),
      'roles' => array(),
    );
    IbRoles::save_configuration();
  }
  
  /**
   * Removes a information column
   *
   * @param string $name The name of the column to remove
   *
   * @return void
   * @author Hugo Wetterberg
   **/
  public static function remove_column($name) {
    if (IbRoles::column_exists($name)) {
      $result = db_query("ALTER TABLE {ib_roles_data} DROP COLUMN column_{$name}");
      if (!$result) {
        throw new Exception(t('Could not remove the column "@name"', array('@name'=>$name)), IB_ROLES_ERR_DATABASE_ERROR);
      }
      
      foreach(IbRoles::$configuration->columns[$name]['groups'] as $group) {
        IbRoles::remove_from_group_helper($group, $name);
      }
      unset(IbRoles::$configuration->columns[$name]);
      IbRoles::save_configuration();
    }
  }
  
  /**
   * Checks if a column is in a group
   *
   * @param string $column The name of the column
   * @param string $group The name of the group
   * 
   * @return void
   * @author Hugo Wetterberg
   **/
  public static function in_group($column, $group) {
    if (IbRoles::column_exists($column)) {
      return in_array($group, IbRoles::$configuration->columns[$column]['groups'], true);
    }
    return false;
  }
  
  /**
   * Adds a column to a group
   *
   * @param string $column The name of the column
   * @param string $group The name of the group
   *
   * @return bool True on success, False on failure (when the column doesn't exist)
   * @author Hugo Wetterberg
   **/
  public static function add_to_group($column, $group) {
    if (IbRoles::column_exists($column)) {
      if (IbRoles::in_group($column, $group)) {
        return true;
      }
      
      IbRoles::$configuration->columns[$column]['groups'][] = $group;
      if (!isset(IbRoles::$configuration->groups[$group])) {
        IbRoles::$configuration->groups[$group] = array($column);
      }
      else {
        IbRoles::$configuration->groups[$group][] = $column;
      }
      
      IbRoles::save_configuration();
      return true;
    }
    return false;
  }
  
  /**
   * Enables direct editing of a column through the 
   * user edit form
   *
   * @param string $column_name The name of the column
   * @param int $weight The weight of the column field
   * @param string $group The group to place the column field in 
   *
   * @return void
   * @author Hugo Wetterberg
   **/
  public static function enable_direct_edit($column_name, $weight=0, $extra=array(), $group='standard') {
    if (IbRoles::column_exists($column_name)) {
      if(IbRoles::$configuration->columns[$column_name]['type'] == IB_ROLES_OBJECT) {
        throw new Exception(t('The column "@name" is a object type and cannot be enabled for direct edit', array('@name'=>$column_name)), IB_ROLES_ERR_TYPE_MISMATCH);
      }
      
      IbRoles::$configuration->columns[$column_name]['direct_edit'] = array(
        'group' => $group,
        'weight' => $weight,
        'extra' => $extra,
      );
      IbRoles::save_configuration();
    }
  }
  
  public static function configure_direct_edit_group($title, $weight=0, $extra=array(), $group='standard') {
    IbRoles::$configuration->edit_groups[$group] = array(
      'title' => $title,
      'weight' => $weight,
      'extra' => $extra,
    );
      
    IbRoles::save_configuration();
  }
  
  /**
   * Gets all columns that have direct edit enabled
   *
   * @return array An associatived array keyed after groups with the columns in subarrays
   * @author Hugo Wetterberg
   **/
  public static function get_direct_edit_columns() {
    $direct_edit = array();
    foreach (IbRoles::$configuration->columns as $column) {
      if (isset($column['direct_edit'])) {
        $direct_edit[$column['direct_edit']['group']][] = $column;
      }
    }
    return $direct_edit;
  }
  
  /**
   * Gets direct edit configuration for a group
   *
   * @param string $group_name The name of the direct edit group
   *
   * @return array The group options
   * @author Hugo Wetterberg
   **/
  public static function get_direct_edit_group($group_name) {
    if (isset(IbRoles::$configuration->edit_groups[$group_name])) {
      return IbRoles::$configuration->edit_groups[$group_name];
    }
    else {
      return array(
        'title' => $group_name,
        'weight' => 0,
        'extra' => array());
    }
  }
  
  /**
   * Alters the user form
   *
   * @param $account object The user object for the form that is altered
   * @param $requirements array Optional parameter that tells user_form to only generate a form that matches the requirements
   * @return void
   * @author Hugo Wetterberg
   **/
  public static function user_form(&$account, $requirements=Null) {
    $form = array();
    $de = IbRoles::get_direct_edit_columns();
    
    $form['_ib_roles_form_update'] = array (
      '#type' => 'hidden',
      '#value' => 'true',
    );
    
    foreach ($de as $group => $columns) {
      $conf = IbRoles::get_direct_edit_group($group);
      $has_columns = false;
      
      $set = array(
        '#type' => 'fieldset',
        '#title' => $conf['title'],
        '#weight' => $conf['weight'],
      );
      foreach ($conf['extra'] as $key => $value) {
        $set[$key] = $value;
      }
      
      foreach ($columns as $column) {
        if (!isset($requirements) || isset($requirements['columns'][$column['name']])) {
          $field = array(
            '#title' => $column['label'],
            '#type' => 'textfield',
            '#weight' => $column['direct_edit']['weight'],
            '#default_value' => isset($account)?$account->ib_roles->$column['name']:'',
            '#required' => isset($requirements),
          );

          foreach ($column['direct_edit']['extra'] as $key => $value) {
            $field[$key] = $value;
          }
          $set['ib_roles_field_'. $column['name']] = $field;
          $has_columns = true;
        }
      }
      
      if ($has_columns) {
        $form['ib_roles_'. $group] = $set;
      }
    }
    
    return $form;
  }
  
  /**
   * Validates the user form
   *
   * @param array $values The array of form values submitted by the user.
   *
   * @return void
   * @author Hugo Wetterberg
   **/
  public static function user_form_validate(&$values) {
    $de = IbRoles::get_direct_edit_columns();
    
    foreach ($de as $group => $columns) {
      foreach ($columns as $column) {
        $field_name = 'ib_roles_field_'. $column['name'];
        $value = trim($values[$field_name]);
        
        if (empty($value))
          $value = null;
        
        if (isset($value)) {
          switch ($column['type']) {
            case IB_ROLES_NUMBER:
              $value = preg_replace('/\s/','',$value);
              if (!is_numeric($value)) {
                form_set_error($field_name, t('The field @label must be a number, was "@value"', array(
                  '@label' => $column['label'],
                  '@value' => $value,
                )));
              }
              break;
          }
        }
      }
    }
  }
  
  /**
   * Returns a array with column values from a form values from a form 
   * created with IbRoles::user_form
   *
   * @return array The submitted values
   * @author Hugo Wetterberg
   **/
  public static function values_from_form(&$values, $clean_up=True) {
    $de = IbRoles::get_direct_edit_columns();
    $update = array();
    if(isset($values['_ib_roles_form_update'])) {
      foreach ($de as $group => $columns) {
        foreach ($columns as $column) {
          $field_name = 'ib_roles_field_'. $column['name'];
          if (isset($values[$field_name])) {
            $value = trim($values[$field_name]);

            if (empty($value))
              $value = null;

            if (isset($value)) {
              switch ($column['type']) {
                case IB_ROLES_NUMBER:
                  $value = preg_replace('/\s/','',$value);
                  break;
              }
            }

            $update[$column['name']] = $value;
            if ($clean_up) {
              unset($values[$field_name]);
            }
          }
        }
      }
      if ($clean_up) {
        unset($values['_ib_roles_form_update']);
      }
    }
    return $update;
  }
  
  /**
   * Handles user updates
   *
   * @return void
   * @author Hugo Wetterberg
   **/
  public static function update_from_user_form(&$values, &$account) {
    $update = IbRoles::values_from_form($values);
    if (count($update)) {
      IbRoles::set_values($account->uid, $update, $values);
    }
  }
  
  /**
   * Disables direct editing of a column through the 
   * user edit form
   *
   * @param string $column_name The name of the column
   *
   * @return void
   * @author Hugo Wetterberg
   **/
  public static function disable_direct_edit($column_name) {
    if (IbRoles::column_exists($column)) {
      unset(IbRoles::$configuration->columns[$column_name]['direct_edit']);
    }
    IbRoles::save_configuration();
  }
  
  /**
   * Gets all groups
   *
   * @return array All configured groups
   * @author Hugo Wetterberg
   **/
  public static function groups() {
    return IbRoles::$configuration->groups;
  }
  
  /**
   * Gets all roles
   *
   * @param string $name Optional name of a role to get
   *
   * @return array All configured roles, or a specific role if $name was supplied
   * @author Hugo Wetterberg
   **/
  public static function roles($name=Null) {
    if (!isset($name)) {
      return IbRoles::$configuration->roles;
    }
    else {
      return IbRoles::$configuration->roles[$name];
    }
  }
  
  /**
   * Gets all columns
   *
   * @return array All configured columns
   * @author Hugo Wetterberg
   **/
  public static function columns() {
    return IbRoles::$configuration->columns;
  }
  
  /**
   * Gets the configuration of ib_roles
   *
   * @return array All configured groups
   * @author Hugo Wetterberg
   **/
  public static function configuration() {
    return clone IbRoles::$configuration;
  }
  
  /**
   * Helper function that removes a column from a group
   * without saving the configuration
   *
   * @param string $group_name The name of the group
   * @param string $column The name of the column
   *  
   * @return void
   * @author Hugo Wetterberg
   **/
  private static function remove_from_group_helper($group_name, $column) {
    $group = &IbRoles::$configuration->groups[$group_name];
    $cc = count($group);
    for ($i=0; $i<$cc; $i++) {
      if ($group[$i]==$column) {
        array_slice($group, $i, 1);
        $i--;
        $cc--;
      }
    }
  }
  
  /**
   * Removes a group without removing any columns
   *
   * @param string $group_name The name of the group
   *
   * @return void
   * @author Hugo Wetterberg
   **/
  private static function remove_group($group_name) {
    $group = &IbRoles::$configuration->groups[$group_name];
    foreach($group as $column) {
      $groups = &IbRoles::$configuration->columns[$name]['groups'];
      $gc = count($groups);
      for ($i=0; $i<$gc; $i++) {
        if ($groups[$i]==$group_name) {
          array_slice($group, $i, 1);
          break;
        }
      }
    }
    IbRoles::save_configuration();
  }
  
  /**
   * Implementation of hook_user[$op='load']
   * Loads all ib_roles data and places it in the ib_roles attribute on
   * the user object
   *
   * @param object $account The user object of the account that is being loaded
   *
   * @return void
   * @author Hugo Wetterberg
   **/
  public static function user_load(&$account) {
    $res = db_query("SELECT * FROM {ib_roles_data} WHERE uid=%d", $account->uid);
    $result = db_fetch_array($res);
    if ($result) {
      $account->ib_roles = new stdClass();
      foreach(IbRoles::$configuration->columns as $name => $column) {
        $account->ib_roles->$name = $result['column_'. $name];
      }
    }
  }
  
  /**
   * Implementation of hook_user[$op='delete']
   *
   * @param object $account The user object of the account that is being deleted
   *
   * @return void
   * @author Hugo Wetterberg
   **/
  public static function user_deleted(&$account) {
    $res = db_query("DELETE FROM {ib_roles_data} WHERE uid=%d", $account->uid);
    unset($account->ib_roles);
  }
  
  /**
   * Sets the value of a single column for a specific user
   *
   * @param int $uid The id of the user to set the information for
   * @param string $name The name of the column
   * @param mixed $value The value to set the column to
   *
   * @return void
   * @author Hugo Wetterberg
   **/
  public static function set_value($uid, $name, $value) {
    IbRoles::set_values($uid, array($name=>$value));
  }
  
  /**
   * Sets the value of a single column for a specific user
   *
   * @param string $name The name of the column
   *
   * @return int One of the IB_ROLES_ type constants or nothing if the column doesn't exist
   * @author Hugo Wetterberg
   **/
  public static function column_type($name) {
    if (IbRoles::column_exists($name)) {
      return IbRoles::$configuration->columns[$name]['type'];
    }
  }
  
  /**
   * Sets the value of several columns for a specific user
   *
   * @param int $uid The id of the user to set the information for
   * @param array $values An associative array with key-value pairs for column names and values
   * @param array $form_values A reference to form values if set_values is called from a user form submit
   *
   * @return void
   * @author Hugo Wetterberg
   **/
  public static function set_values($uid, $values, &$form_values=Null) {
    //Create a insert array with the validated values
    $inserts = array();
    foreach ($values as $name => $value) {
      if (!IbRoles::column_exists($name)) {
        throw new Exception(t('The column "@name" doesn\'t exist', array('@name'=>$name)), IB_ROLES_ERR_COLUMN_DOESNT_EXIST);
      }
      
      if (isset($value)) {
        $type = IbRoles::column_type($name);
        //Validate the value and determine wich placeholder that should be used with db_query
        switch ($type) {
          case IB_ROLES_NUMBER:
            if (!is_numeric($value)) {
              throw new Exception(t('The value "@value" doesn\'t match the type of the column "@name"', array(
                '@value'=>$value,
                '@name'=>$name )), IB_ROLES_ERR_TYPE_MISMATCH);
            }
            $placeholder = "%d";
            break;
          case IB_ROLES_OBJECT:
            $value = serialize($value);
            $placeholder = "'%s'";
            break;
          default:
            $placeholder = "'%s'";
        }
      }
      else {
        $placeholder = "NULL";
      }
      
      $inserts[$name] = array('placeholder'=>$placeholder, 'value'=>$value);
    }
    
    //Load the user, throw exception if it doesn't exist
    $user = user_load(array('uid'=>$uid));
    if (!$user) {
      throw new Exception(t('The user "@uid" doesn\'t exist', array('@uid'=>$uid)), IB_ROLES_ERR_NO_SUCH_USER);
    }
    
    if ($user->ib_roles) {
      //Construct and execute an update query from the inserts array
      $sets = array();
      $args = array();
      foreach($inserts as $name => $spec) {
        $sets[] = "column_{$name}={$spec['placeholder']}";
        if(isset($spec['value'])) {
          $args[] = $spec['value'];
        }
      }
      array_unshift($args, "UPDATE {ib_roles_data} SET ". join(", ", $sets) ." WHERE uid=%d");
      array_push($args, $user->uid);
      
      if (!call_user_func_array('db_query', $args)) {
        throw new Exception(t('Could not update information for the user "@name"', array('@name'=>$user->name)), IB_ROLES_ERR_DATABASE_ERROR);
      }
    }
    else {
      $names = array();
      $placeholders = array();
      $args = array($user->uid);
      
      foreach($inserts as $name => $spec) {
        $names[] = 'column_'. $name;
        $placeholders[] = $spec['placeholder'];
        if(isset($spec['value'])) {
          $args[] = $spec['value'];
        }
      }
      array_unshift($args, "INSERT INTO {ib_roles_data}(uid, ". join(", ", $names) .") VALUES(%d, ". join(", ", $placeholders) .")");
            
      if (!call_user_func_array('db_query', $args)) {
        throw new Exception(t('Could not insert information for the user "@name"', array('@name'=>$user->name)), IB_ROLES_ERR_DATABASE_ERROR);
      }
    }
    
    IbRoles::update_roles($uid, $form_values);
  }
  
  /**
   * Clears all role rules
   *
   * @return void
   * @author Hugo Wetterberg
   **/
  public static function clear_role_rules() {
    IbRoles::$configuration->roles = array();
    IbRoles::save_configuration();
  }
  
  /**
   * Adds a role requirement 
   *
   * @param string $role The role to add requirements for
   * @param array $groups The groups that are required for this role
   * @param array $fields 
   * The names of the individual fields that are required for this role
   * Fields names can be prefixed with "user:" to check the user object attributes
   * "user:mail" can be used to check for email and "user:data/mimemail_textonly"
   * could be used to check if the user has configured mime mail.
   * The mime mail example could be made more useful by prefixing "[t]" or "[f]" to use additional
   * boolean checking. "[f]user:data/mimemail_textonly" could be use to only match users that allow
   * html emails.
   * @param bool $reversible True if ib_roles may remove the role from users, False if roles only may be given
   *
   * @return void
   * @author Hugo Wetterberg
   **/
  public static function configure_role_requirements($role, $groups=array(), $fields=array(), $reversible=true, $lightweight=true) {
    if (!is_array($groups)) {
      $groups = array();
    }
    
    //Analyze the raw fields
    $columns = array();
    $attributes = array();
    foreach ($fields as $field) {
      $matches = array();
      if (preg_match('/^(\[[^\]]\])?(user\:)?(.+)$/',$field,$matches)) {
        if (empty($matches[1]) && !$matches[2]) {
          $columns[$matches[3]] = true;
        }
        else {
          $f = array();
          if (!empty($matches[1])) {
            $f['flags'] = trim(mb_convert_case($matches[1], MB_CASE_LOWER),'[]');
          }
          
          if ($matches[2]) {
            $f['path'] = explode('/',$matches[3]);
            $attributes[] = $f;
          }
          else {
            $f['name'] = $matches[3];
            $columns[$matches[3]] = $f;
          }
        }
      }
    }
    
    IbRoles::$configuration->roles[$role] = array(
      'groups' => $groups,
      'columns' => $columns,
      'attributes' => $attributes,
      'reversible' => $reversible,
      'lightweight' => $lightweight,
    );
    IbRoles::save_configuration();
  }
  
  /**
   * Builds requirement arrays from the requirement configuration
   *
   * @param string $role Optional role to get the requirements for
   *
   * @return array 
   * An array containing all roles => requirements if $role was omitted
   * Otherwise an array containing the requirements for the role is returned
   *
   * @author Hugo Wetterberg
   **/
  public static function build_requirements($role=Null) {
    if (empty($role)) {
      $requirements = array();
      foreach (IbRoles::$configuration->roles as $role => $requirements) {
        $requirements[$role] = IbRoles::build_requirements_helper($requirements);
      }
      return $requirements;
    }
    else {
      return IbRoles::build_requirements_helper(IbRoles::$configuration->roles[$role]);
    }
  }
  
  /**
   * Builds requirement array from the requirement configuration
   *
   * @param string $configuration The configuration to get the requirements for
   *
   * @return array An array containing the requirements for the configuration
   * @author Hugo Wetterberg
   **/
  private static function build_requirements_helper($configuration) {
    $columns = $configuration['columns'];
    
    foreach ($configuration['groups'] as $group) {
      foreach (IbRoles::$configuration->groups[$group] as $column) {
        $columns[$column] = true;
      }
    }
    
    return array(
      'attributes'=> $configuration['attributes'],
      'columns' => $columns,
    );
  }
  
  /**
   * Used to fetch values from mixed array and object trees
   *
   * @param array|object The root object/array
   * @param array An array describing the path explode('/','this/is/a/path')
   *
   * @return mixed The value or null if the value wasn't found
   * @author Hugo Wetterberg
   **/
  public static function navigate_object($object, $path) {
    $segments = count($path);
    for ($i=0; $i<$segments; $i++) {
      if (is_object($object)) {
        if (isset($object->$path[$i])) {
          $object = $object->$path[$i];
        }
        else {
          return Null;
        }
      }
      else if (is_array($object)) {
        if (isset($object[$path[$i]])) {
          $object = $object[$path[$i]];
        }
      }
      else {
        return Null;
      }
    }
    return $object;
  }
  
  private static function flag_checks($flags, $value) {
    for($i=0; $i<count($flags); $i++) {  
      switch($flags[$i]) {
        case 't':
          if (!$value) {
            return False;
          }
          break;
        case 'f':
          if ($value) {
            return False;
          }
          break;
      }
    }
    return True;
  }
  
  /**
   * Updates the roles the user belongs to according to the defined rules
   *
   * @param int $uid The user id
   * @param array $form_values A reference to form values if set_values is called from a user form submit
   *
   * @return void
   * @author Hugo Wetterberg
   **/
  public static function update_roles($uid, &$values=Null) {
    $user_roles = user_roles();
    $user = user_load(array('uid'=>$uid));
    $user->data = unserialize($user->data);
    
    if (isset($values) && isset($values['roles'])) {
      $roles = $values['roles'];
    }
    else {
      $roles = $user->roles;
    }
    
    foreach (IbRoles::$configuration->roles as $role => $configuration) {
      $requirements = IbRoles::build_requirements_helper($configuration);
      
      $is_eligible = true;
      
      if ($configuration['reversible'] || !$is_current) {
        foreach ($requirements['columns'] as $name => $check) {
          if(!isset($user->ib_roles->$name)) {
            $is_eligible = false;
            break;
          }
          else if (is_array($check)){
            if (isset($check['flags'])) {
              if (!IbRoles::flag_checks($check['flags'], $user->ib_roles->$name)) {
                $is_eligible = false;
                break;
              }
            }
          }
        }

        if ($is_eligible) {
          foreach($requirements['attributes'] as $name => $check) {
            $value = IbRoles::navigate_object($user, $check['path']);
            if (!isset($value) || (is_string($value) && empty($value))) {
              $is_eligible = false;
              break;
            }
            else if (isset($check['flags'])) {
              if (!IbRoles::flag_checks($check['flags'], $value)) {
                $is_eligible = false;
                break;
              }
            }
          }
        }
      }
      
      if($is_eligible) {
        $rid = array_search($role, $user_roles);
        $roles[$rid] = $role;
      }
      else if (!$is_eligible && $configuration['reversible']) {
        $rid = array_search($role, $user_roles);
        unset($roles[$rid]);
      }
    }
    
    if (!isset($values)) {
      user_save($user, array('roles'=>$roles));
    }
    else {
      $values['roles'] = $roles;
    }
  }
}